package com.juqimiao.outposts.spring.oauth2.api.configuration;

import com.juqimiao.outposts.spring.oauth2.mobile.GrantTypes;
import com.juqimiao.outposts.spring.oauth2.mobile.token.MobileCodeTokenGranter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * 配置授权认证服务类。
 * @Description 主要配置和OAuth2相关的配置。
 * 包括：
 * 对相关OAuth授权接口
 * /oauth/authorize,
 * /oauth/token,
 * /oauth/check_token,
 * /oauth/confirm_access,
 * /oauth/error
 * 接口的暴露和安全访问设置
 */
@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 支持password授权方式的认证控制器。
     */
    @Autowired
    private AuthenticationManager authenticationManager;
    /**
     * 提供自定义的用户信息的服务。
     */
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired

    @Resource(name = "OAuthClientDetailServiceImpl")
    private ClientDetailsService clientDetailsService;

    @Resource(name = "CrossDomainCorsConfigurationSource")
    private CorsConfigurationSource corsConfigurationSource;

    @Bean
    public ClientDetailsService getClientDetailsService(){
        return clientDetailsService;
    }
    /**
     * 配置oauth2服务跨域
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        return corsConfigurationSource;
    }
    /**
     * 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务.
     * 核心配置，在启动时就会进行配置.
     *
     * @param endpoints 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)
     * @throws Exception Exception.
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        //使用Jwt Token.
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbGfA0XdkIpK5h2O9mg5o35pit\n" +
                "xwiHDnlpBTCTUH+pkGMdDe6d9nVQDr61QUEMWAgbnb/irTXh5VigGhHDbG/4kmVy\n" +
                "1BgSfLxUx50jmm7jnvnS4Hrb65g920x26gaBW+I9n9cHF/QShrqaNXP9DDeqhqNz\n" +
                "dmrkaaAQQkQ9liN6awIDAQAB");

        //调用afterPropertiesSet确保verifier被初始化，verifier将在验证token有效性的时候使用到。
        converter.afterPropertiesSet();
        JwtTokenStore store = new JwtTokenStore(converter);
        /**
         * 这里只需要配置AuthenticationManager以便支持grant_type == password的认证方式。
         */
        endpoints.authenticationManager(authenticationManager);
        /**
         * 使用jwt token。Spring OAuth2
         */
        endpoints.accessTokenConverter(converter)
                .tokenStore(store);
        endpoints.tokenGranter(tokenGranter(endpoints));
        endpoints.userDetailsService(userDetailsService);
        endpoints.reuseRefreshTokens(false);
        super.configure(endpoints);

    }

    /**
     * 这里配置client信息存储的方式。
     * @Description Client信息存储的方式有三种：
     * InMemory：保存在内存中。
     * JDBC：配置JDBC链接保存在数据库中的方式。
     * ClientDetailService：自定义Client存储的方式。
     *
     * @param clients 用来配置客户端详情服务（ClientDetailsService,
     *                客户端详情信息在这里进行初始化，
     *                你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
     * @throws Exception Exception.
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService).build();
    }

    /**
     * 配置授权服务相关endpoint的访问权限.
     * @param security 用来配置令牌端点(Token Endpoint)的安全约束.
     * url:/oauth/token_key,exposes public key for token verification if using JWT tokens
     * @throws Exception 异常.
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security)
            throws Exception {

        CorsConfigurationSource source = new CorsConfigurationSource() {
            @Override
            public CorsConfiguration getCorsConfiguration(HttpServletRequest httpServletRequest) {
                CorsConfiguration corsConfiguration = new CorsConfiguration();
                corsConfiguration.addAllowedHeader("*");
                corsConfiguration.addAllowedOrigin("*");
                corsConfiguration.addAllowedMethod("*");
                corsConfiguration.setAllowCredentials(true);
                corsConfiguration.setMaxAge(3600L);
                return corsConfiguration;
            }
        };

        List<Filter> authenticationFilters = new ArrayList<>();
        //添加非跨域请求过滤。
        authenticationFilters.add(new CorsFilter(source));

        //url:/oauth/token to get access token
        security.tokenKeyAccess("isAuthenticated()")
                //url:/oauth/check_token allow check token
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients()
                .tokenEndpointAuthenticationFilters(authenticationFilters);

    }

    private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
        List<TokenGranter> granters = new ArrayList<TokenGranter>(Arrays.asList(endpoints.getTokenGranter()));
        granters.add(new MobileCodeTokenGranter(authenticationManager,
                endpoints.getTokenServices(),
                endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(),
                GrantTypes.MOBILE));
        return new CompositeTokenGranter(granters);
    }
}
